package cn.wqb.bpp.common.jwt;

import cn.wqb.bpp.common.utils.LogUtils;
import cn.wqb.bpp.common.utils.RedisUtils;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import java.security.MessageDigest;
import java.util.*;

/**
 * 依赖
 * <dependency>
 * <groupId>io.jsonwebtoken</groupId>
 * <artifactId>jjwt</artifactId>
 * <version>0.9.1</version>
 * </dependency>
 */

@Component
public class JWTUtils {

    private final static int EXPIRATION_SECONDS = 604800;//秒（7天）

    private static String usernameForKey(String username) {
        String newUsername = username + "_secret";
        return newUsername;
    }

    /**
     * 创建新Token
     *
     * @param useNewSecret 在重新登录和修改密码时需要true
     * @return
     */
    public static Token createToken(String account, String ip, boolean useNewSecret) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("account", account);
        claims.put("ip", ip);
        //System.out.println("=============客户端IP:" + clientIP);
        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        c.add(Calendar.SECOND, EXPIRATION_SECONDS);
        Date d = c.getTime();
        String secret = null;
        if (useNewSecret) {
            secret = createNewSecretAndSaveToDB(account);
        } else {
            secret = getSecretByUsernameFromDB(account);
            if (secret == null) {
                secret = createNewSecretAndSaveToDB(account);
            }
        }
        if (secret != null) {
            String jwt = Jwts.builder().setClaims(claims).setExpiration(d).signWith(SignatureAlgorithm.HS512, secret).compact();
            Token token = new Token();
            token.setAccount(account);
            token.setToken(jwt);
            token.setExpireSeconds(EXPIRATION_SECONDS);
            return token;
        }
        return null;
    }

    /**
     * 解析客户传过来的Token，成功返回新的true，失败返回false
     *
     * @param token
     * @return
     */
    public static boolean parseToken(Token token, String ip) {
        if (token != null && token.getAccount() != null) {
            try {
                String secret = getSecretByUsernameFromDB(token.getAccount());
                if (secret != null) {
                    Map<String, Object> body = Jwts.parser().setSigningKey(secret).parseClaimsJws(token.getToken()).getBody();
                    String _ip = (String) body.get("ip");
                    //发来token的客户端IP和token里的ip匹配才能通过验证
                    if (_ip.equals(ip)) {
                        return true;
                    }
                }
            } catch (Exception ex) {
                System.out.println("===========token验证不通过");
            }
        }
        return false;
    }

    /**
     * 解析客户传过来的Token，成功返回新的Token，失败返回null
     *
     * @param token
     * @return
     */
    public static Token parseAndReturnNewToken(Token token, String ip) {
        if (token != null && token.getAccount() != null) {
            try {
                String secret = getSecretByUsernameFromDB(token.getAccount());
                if (secret != null) {
                    Map<String, Object> body = Jwts.parser().setSigningKey(secret).parseClaimsJws(token.getToken()).getBody();
                    String _ip = (String) body.get("ip");
                    //发来token的客户端IP和token里的ip匹配才能通过验证
                    if (_ip.equals(ip)) {
                        String account = (String) body.get("account");
                        return createToken(account, _ip, false);
                    }
                }
            } catch (Exception ex) {
                System.out.println("===========token验证不通过");
            }
        }
        return null;
    }

    /**
     * 从数据库删除Secret（注销或更改密码可用）
     *
     * @param username
     */
    public static boolean removeSecret(String username) {
        RedisUtils.setValue(usernameForKey(username), null);
        return true;
    }

    //创建一个新Secret并捆绑用户名保存到数据库
    private static String createNewSecretAndSaveToDB(String username) {
        try {
            UUID uuid = UUID.randomUUID();
            String secret = getMD5(uuid.toString()).toUpperCase();
            RedisUtils.setValue(usernameForKey(username), secret);
            return secret;
        } catch (Exception e) {
            LogUtils.instance(JWTUtils.class).error(e + "：写入Redis失败");
        }
        return null;
    }


    //从数据库获取指定用户的Secret
    private static String getSecretByUsernameFromDB(String username) {
        try {
            String secret = (String) RedisUtils.getValue(usernameForKey(username));
            return secret;
        } catch (Exception e) {
            LogUtils.instance(JWTUtils.class).error(e + ">>>>>>>>>>Cause:" + e.getCause());
        }
        return null;
    }

    private static String getMD5(String value) {
        if (value != null) {
            try {
                MessageDigest messageDigest = MessageDigest.getInstance("md5");
                messageDigest.update(value.getBytes());
                return getFormattedText(messageDigest.digest());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private static String getFormattedText(byte[] bytes) {
        int len = bytes.length;
        StringBuilder buf = new StringBuilder(len * 2);
        for (int j = 0; j < len; j++) {
            buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
            buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
        }
        return buf.toString();
    }

    private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

}
